﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;

namespace Tomahawk.Runtime.Logic.UI
{
    /// <summary>
    /// Represents a user interface object that can be placed inside an UIScene.
    /// This kind of controls has screen coordinates and other properties to control its positioning.
    /// They can be also focusable.
    /// </summary>
    public class XUIPlaceableControl : XUIControl
    {

        #region "Enumeration"

        /// <summary>
        /// Available docking modes. 
        /// This controls how the control is shaped against his parent.
        /// </summary>
        public enum DockingModes
        {
            Top,
            TopLeft,
            TopRight,
            Left,
            Right,
            Bottom,
            BottomLeft,
            BottomRight,
            Fill
        }

        #endregion

        #region "Serializable Properties"

        /// <summary>
        /// This structs stores the positioning information for the control
        /// </summary>
        public struct PositioningInfo
        {
            [XmlAttribute("top")]
            public float Top;

            [XmlAttribute("left")]
            public float Left;

            [XmlAttribute("width")]
            public float Width;

            [XmlAttribute("height")]
            public float Height;

            [XmlAttribute("docking")]
            public DockingModes Docking;
        }

        /// <summary>
        /// Position of the object relative to its parent
        /// </summary>
        [XmlElement("Position")]
        public PositioningInfo Position;

        #endregion

        /// <summary>
        /// Make this object pseudo-abstract by protecting the constructor
        /// </summary>
        protected XUIPlaceableControl()
        {
            //defaults value for positioning
            this.Position.Docking = DockingModes.TopLeft;
        }

        /// <summary>
        /// Initialization
        /// </summary>
        protected override void OnInitialize()
        {
            base.OnInitialize();

            //compute positioining
            this.computePosition();
        }

        #region "Positioning"

        private Rectangle parentRenderRect;

        /// <summary>
        /// Computed render position
        /// </summary>
        protected Rectangle renderRect;

        /// <summary>
        /// Use this method to update control position
        /// </summary>
        public void SetPosition(float newLeft, float newTop, float newWidth, float newHeight)
        {
            this.Position.Left = newLeft;
            this.Position.Top = newTop;
            this.Position.Width = newWidth;
            this.Position.Height = newHeight;
            this.computePosition();
        }

        /// <summary>
        /// Override this to provide custom position change logic
        /// </summary>
        protected virtual void OnPositionChange()
        {
        }

        /// <summary>
        /// Compute the new position
        /// </summary>
        protected void computePosition()
        {
            //
            //NOTE: All asignations are inlined for performance reasons
            //

            //use viewport's size as default parent's size/position
            this.parentRenderRect.X = 0;
            this.parentRenderRect.Y = 0;
            this.parentRenderRect.Width = Engine.Instance.GraphicsDevice.Viewport.Width;
            this.parentRenderRect.Height = Engine.Instance.GraphicsDevice.Viewport.Height;

            //get parent's rect (if any)
            XUIPlaceableControl placeableParent = this.parent as XUIPlaceableControl;
            if (placeableParent != null) 
            {
                this.parentRenderRect.X = placeableParent.renderRect.X;
                this.parentRenderRect.Y = placeableParent.renderRect.Y;
                this.parentRenderRect.Width = placeableParent.renderRect.Width;
                this.parentRenderRect.Height = placeableParent.renderRect.Height;
            }

            //compute current rect
            switch (this.Position.Docking)
            {
                case DockingModes.Top:
                    this.renderRect.X = parentRenderRect.X;
                    this.renderRect.Y = parentRenderRect.Y;
                    this.renderRect.Width = parentRenderRect.Width;
                    this.renderRect.Height = (int)this.Position.Height;
                    break;

                case DockingModes.Bottom:
                    this.renderRect.X = parentRenderRect.X;
                    this.renderRect.Y = (int)(parentRenderRect.Top + parentRenderRect.Height - this.Position.Height);
                    this.renderRect.Width = parentRenderRect.Width;
                    this.renderRect.Height = (int)this.Position.Height;
                    break;

                case DockingModes.Left:
                    this.renderRect.X = parentRenderRect.X;
                    this.renderRect.Y = (int)(parentRenderRect.Top + parentRenderRect.Height - this.Position.Height);
                    this.renderRect.Width = parentRenderRect.Width;
                    this.renderRect.Height = (int)this.Position.Height;
                    break;

                case DockingModes.Right:
                    this.renderRect.X = (int)(parentRenderRect.Width - this.Position.Width);
                    this.renderRect.Y = parentRenderRect.Y;
                    this.renderRect.Width = (int)this.Position.Width;
                    this.renderRect.Height = parentRenderRect.Height;
                    break;

                case DockingModes.Fill:
                    this.renderRect.X = parentRenderRect.X;
                    this.renderRect.Y = parentRenderRect.Y;
                    this.renderRect.Width = parentRenderRect.Width;
                    this.renderRect.Height = parentRenderRect.Height;
                    break;

                case DockingModes.TopLeft:
                    this.renderRect.X = (int)(this.Position.Left + parentRenderRect.X);
                    this.renderRect.Y = (int)(this.Position.Top + parentRenderRect.Y);
                    this.renderRect.Width = (int)(this.Position.Width);
                    this.renderRect.Height = (int)(this.Position.Height);
                    break;

                case DockingModes.TopRight:
                    this.renderRect.X = (int)((parentRenderRect.X + parentRenderRect.Width) - (this.Position.Width + this.Position.Left));
                    this.renderRect.Y = (int)(this.Position.Top + parentRenderRect.Y);
                    this.renderRect.Width = (int)(this.Position.Width);
                    this.renderRect.Height = (int)(this.Position.Height);
                    break;

                case DockingModes.BottomLeft:
                    this.renderRect.X = (int)(this.Position.Left + parentRenderRect.X);
                    this.renderRect.Y = (int)((parentRenderRect.Y + parentRenderRect.Height) - (this.Position.Height + this.Position.Top));
                    this.renderRect.Width = (int)(this.Position.Width);
                    this.renderRect.Height = (int)(this.Position.Height);
                    break;

                case DockingModes.BottomRight:
                    this.renderRect.X = (int)((parentRenderRect.X + parentRenderRect.Width) - (this.Position.Width + this.Position.Left));
                    this.renderRect.Y = (int)((parentRenderRect.Y + parentRenderRect.Height) - (this.Position.Height + this.Position.Top));
                    this.renderRect.Width = (int)(this.Position.Width);
                    this.renderRect.Height = (int)(this.Position.Height);
                    break;

            }

            //compute childs rects
            foreach (XUIControl child in this.ChildControls)
            {
                XUIPlaceableControl placeableChild = child as XUIPlaceableControl;
                if (placeableChild != null)
                    placeableChild.computePosition();
            }

            //notify derived class
            this.OnPositionChange();

        }

        #endregion

        #region "Focus Management"

        public struct FocusReference
        {
            [XmlAttribute("left")]
            public string Left;

            [XmlAttribute("right")]
            public string Right;

            [XmlAttribute("up")]
            public string Up;

            [XmlAttribute("down")]
            public string Down;
        }

        [XmlElement("Focus")]
        public FocusReference FocusReferences;

        [XmlElement("CanFocus")]
        public bool CanHaveFocus = true;

        private bool focused = false;

        public bool Focused
        {
            get{ return focused;}
        }

        internal void Unfocus()
        {
            this.focused = false;
            this.OnUnfocus();
        }

        protected virtual void OnUnfocus() { }

        protected virtual void OnFocus() { }

        public void Focus()
        {
            //set this control as the focused one
            if (CanHaveFocus)
            {
                this.focused = true;
                this.OnFocus();
                this.ParentUIScene.SetFocusedControl(this);
            }
            else
            {
                //let the parent UIScene determine what's the next control to be focused
                this.ParentUIScene.FocusNavigateNext(this);
            }
        }

        #endregion

        #region "User activation"

        /// <summary>
        /// Determine if this controls can handle user activation 
        /// </summary>
        protected bool enabledUserActivation = true;

        /// <summary>
        /// Call this metod to notify the control with an user activation
        /// </summary>
        public void UserActivate()
        {
            if (this.enabledUserActivation)
                this.OnUserActivate();
        }

        /// <summary>
        /// Override this to provide custom activation logic
        /// </summary>
        protected virtual void OnUserActivate() { }

        #endregion

    }

}
